﻿using Microsoft.Win32;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace AppleMagicTrackpadControlPanel
{
    public partial class FormATPControl : Form
    {
        private bool AutoStart = false;
        private MyFunction AMTPFunc;
        private bool LoadDone = false;

        private Point LeftButtonLabel;
        private Point RightButtonLabel;

        public FormATPControl()
        {
            InitializeComponent();
        }

        private void FormATPControl_Load(object sender, EventArgs e)
        {
            LeftButtonLabel = this.labelLeftButton.Location;
            RightButtonLabel = this.labelRightButton.Location;

            try
            {
                AMTPFunc = new MyFunction(false);

                using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true))
                {
                    if (key == null)
                    {
                        this.checkBoxStartup.Checked = false;
                    }
                    if (key.GetValue("AMTCPbyAbsoluteDomain") != null)
                    {
                        this.checkBoxStartup.Checked = true;
                    }
                    else
                        this.checkBoxStartup.Checked = false;
                    key.Close();
                }
            }
            catch (Exception err)
            {
                MessageBox.Show("读取保存的 Apple Magic Trackpad 配置失败,加载默认设置!" + Environment.NewLine + "错误:" + ((null == err.InnerException) ? err.Message : err.InnerException.Message), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                AMTPFunc = new MyFunction();
            }
            this.trackBarScrollSpeed.Value = AMTPFunc.WheelScrollLines;
            this.labelScrollSpeed.Text = AMTPFunc.WheelScrollLines.ToString();

            this.trackBarTrackSpeed.Value = AMTPFunc.MouseTrackSpeed;
            this.labelTrackSpeed.Text = AMTPFunc.MouseTrackSpeed.ToString();

            this.checkBoxOneTap.Checked = AMTPFunc.Options.HasFlag(MyFunction.Child.TapToClick);
            this.checkBoxTwoTap.Enabled = this.checkBoxOneTap.Checked;
            this.checkBoxOneDrag.Checked = AMTPFunc.Options.HasFlag(MyFunction.Child.Dragging);
            this.checkBoxOneDragLock.Enabled = this.checkBoxOneDrag.Checked;
            this.checkBoxOneDragLock.Checked = AMTPFunc.Options.HasFlag(MyFunction.Child.DragLock);

            this.checkBoxTwoTap.Checked = AMTPFunc.Options.HasFlag(MyFunction.Child.RightClick);
            this.checkBoxTwoScroll.Checked = AMTPFunc.Options.HasFlag(MyFunction.Child.Scroll);

            this.checkBoxEnableRightButton.Checked = AMTPFunc.Options.HasFlag(MyFunction.Child.RightButton);
            this.checkBoxSwapButton.Checked = AMTPFunc.Options.HasFlag(MyFunction.Child.SwapButton);

            LoadDone = true;

            try
            {
                AMTPFunc.Options = AMTPFunc.Options;
            }
            catch (Exception err)
            {
                MessageBox.Show("用保存的设置更新 Apple Magic Trackpad 失败!" + Environment.NewLine + "错误:" + ((null == err.InnerException) ? err.Message : err.InnerException.Message), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            string[] cmd = System.Environment.GetCommandLineArgs();
            if (cmd.Length > 1 && cmd[1] == "/AutoStart")
            {
                AutoStart = true;
                this.Visible = false;
                this.Hide();
            }
        }
        private void FormATPControl_Activated(object sender, EventArgs e)
        {
            if (AutoStart)
            {
                this.Hide();
                notifyIconMain.ShowBalloonTip(2000, this.notifyIconMain.BalloonTipTitle, "Apple Magic Trackpad Control Panel 已经运行!", ToolTipIcon.Info);
            }
        }

        private void trackBarTrackSpeed_Scroll(object sender, EventArgs e)
        {
            try
            {
                this.labelTrackSpeed.Text = this.trackBarTrackSpeed.Value.ToString();
                AMTPFunc.MouseTrackSpeed = this.trackBarTrackSpeed.Value;
            }
            catch (Exception err)
            {
                MessageBox.Show("改变鼠标指针移动速度失败!" + Environment.NewLine + "错误:" + ((null == err.InnerException) ? err.Message : err.InnerException.Message), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void trackBarScrollSpeed_Scroll(object sender, EventArgs e)
        {
            try
            {
                this.labelScrollSpeed.Text = this.trackBarScrollSpeed.Value.ToString();
                AMTPFunc.WheelScrollLines = this.trackBarScrollSpeed.Value;
                AMTPFunc.WheelScrollChars = this.trackBarScrollSpeed.Value;
            }
            catch (Exception err)
            {
                MessageBox.Show("改变滚动行列数量失败!" + Environment.NewLine + "错误:" + ((null == err.InnerException) ? err.Message : err.InnerException.Message), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void notifyIconMain_DoubleClick(object sender, EventArgs e)
        {
            AutoStart = false;
            this.Show();
        }

        private void FormATPControl_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = true;
            this.Hide();
            notifyIconMain.ShowBalloonTip(2000, this.notifyIconMain.BalloonTipTitle, "Apple Magic Trackpad Control Panel 在后台运行.", ToolTipIcon.Info);
        }
        private void buttonClose_Click(object sender, EventArgs e)
        {
            this.Hide();
        }

        private void toolStripMenuItemExit_Click(object sender, EventArgs e)
        {
            FormClosing -= FormATPControl_FormClosing;
            this.Close();
        }

        private void checkBoxALL_CheckedChanged(object sender, EventArgs e)
        {
            if (!LoadDone) { return; }
            try
            {
                AMTPFunc.Options = checkBoxOneTap.Checked ? (AMTPFunc.Options | MyFunction.Child.TapToClick) : (AMTPFunc.Options & (~MyFunction.Child.TapToClick));
                this.checkBoxTwoTap.Enabled = checkBoxOneTap.Checked;

                AMTPFunc.Options = checkBoxOneDrag.Checked ? (AMTPFunc.Options | MyFunction.Child.Dragging) : (AMTPFunc.Options & (~MyFunction.Child.Dragging));
                this.checkBoxOneDragLock.Enabled = checkBoxOneDrag.Checked;

                AMTPFunc.Options = checkBoxOneDragLock.Checked ? (AMTPFunc.Options | MyFunction.Child.DragLock) : (AMTPFunc.Options & (~MyFunction.Child.DragLock));

                AMTPFunc.Options = checkBoxTwoScroll.Checked ? (AMTPFunc.Options | MyFunction.Child.Scroll) : (AMTPFunc.Options & (~MyFunction.Child.Scroll));

                AMTPFunc.Options = checkBoxTwoTap.Checked ? (AMTPFunc.Options | MyFunction.Child.RightClick) : (AMTPFunc.Options & (~MyFunction.Child.RightClick));


                if (checkBoxSwapButton.Checked)
                {
                    AMTPFunc.Options = AMTPFunc.Options | MyFunction.Child.SwapButton;
                    this.labelRightButton.Location = LeftButtonLabel;
                    this.labelLeftButton.Location = RightButtonLabel;
                    this.Refresh();
                }
                else
                {
                    AMTPFunc.Options = AMTPFunc.Options & (~MyFunction.Child.SwapButton);
                    this.labelLeftButton.Location = LeftButtonLabel;
                    this.labelRightButton.Location = RightButtonLabel;
                    this.Refresh();
                }

                if (checkBoxEnableRightButton.Checked)
                {
                    AMTPFunc.Options = AMTPFunc.Options | MyFunction.Child.RightButton;
                    this.labelRightButton.Visible = true;
                    this.Refresh();
                }
                else
                {
                    AMTPFunc.Options = AMTPFunc.Options & (~MyFunction.Child.RightButton);
                    this.labelRightButton.Visible = false;
                    this.Refresh();
                }
            }
            catch (Exception err)
            {
                MessageBox.Show("更新 Apple Magic Trackpad 配置失败!" + Environment.NewLine + "错误:" + ((null == err.InnerException) ? err.Message : err.InnerException.Message), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void buttonSave_Click(object sender, EventArgs e)
        {
            try
            {
                if (!AMTPFunc.SaveAllConfig())
                    MessageBox.Show("保存配置失败!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                else
                    notifyIconMain.ShowBalloonTip(2000, this.notifyIconMain.BalloonTipTitle, "成功保存配置,下次启动本程序将自动应用当前配置!", ToolTipIcon.Info);
            }
            catch (Exception err)
            {
                MessageBox.Show("保存配置失败!" + Environment.NewLine + "错误:" + ((null == err.InnerException) ? err.Message : err.InnerException.Message), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void checkBoxStartup_CheckedChanged(object sender, EventArgs e)
        {
            try
            {
                if (checkBoxStartup.Checked)
                {
                    using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true))
                    {
                        if (key == null)
                        {
                            key.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run");
                        }
                        key.SetValue("AMTCPbyAbsoluteDomain", "\""+Application.ExecutablePath + "\" /AutoStart", RegistryValueKind.String);
                        key.Close();
                    }
                }
                else
                {
                    using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true))
                    {
                        if (key == null)
                        {
                            throw new Exception("The SubKey provided doesnt exist! Please check your entry and try again");
                        }
                        key.DeleteValue("AMTCPbyAbsoluteDomain", true);
                        key.Close();
                    }
                }
            }
            catch (Exception err)
            {
                MessageBox.Show("设置开机自动运行状态失败!" + Environment.NewLine + "错误:" + err.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void linkLabelCP_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start("http://www.weibo.com/nonegfw");
        }
    }

    public class MyFunction
    {
        /// <summary>
        /// 鼠标配置信息注册表子键
        /// Control Panel\Mouse
        /// </summary>
        public string MouseRegSubKey { get { return "Control Panel\\Mouse"; } }
        /// <summary>
        /// 鼠标指针移动速度项名
        /// MouseSensitivity
        /// </summary>
        public string MouseTrackSpeedRegName { get { return "MouseSensitivity"; } }
        /// <summary>
        /// 滚动速度信息注册表子键
        /// Control Panel\Desktop
        /// </summary>
        public string WheelScrollRegSubKey { get { return "Control Panel\\Desktop"; } }
        /// <summary>
        /// 行滚动速度项名
        /// WheelScrollLines
        /// </summary>
        public string WheelScrollLineRegName { get { return "WheelScrollLines"; } }
        /// <summary>
        /// 列滚动速度项名
        /// WheelScrollChars
        /// </summary>
        public string WheelScrollCharRegName { get { return "WheelScrollChars"; } }
        /// <summary>
        /// 触控板设备路径
        /// \\.\AppleWirelessTrackpad
        /// </summary>
        public string AMTPDeviceURL { get { return "\\\\.\\AppleWirelessTrackpad"; } }
        /// <summary>
        /// 触控板信息注册表子键
        /// Software\Apple Inc.\Trackpad
        /// </summary>
        public string AMTPDeviceRegSubKey { get { return "Software\\Apple Inc.\\Trackpad"; } }
        /// <summary>
        /// 触控板配置项名
        /// </summary>
        public string AMTPDeviceRegName { get { return "Mode"; } }

        private int _MouseTrackSpeed = 10;
        /// <summary>
        /// 鼠标指针移动速度
        /// 默认为注册表的值
        /// 无法读取注册表时为10
        /// </summary>
        public int MouseTrackSpeed
        {
            get { return _MouseTrackSpeed; }
            set
            {
                try
                {
                    UpdateSysParamInfo(0x0071, 0, value, false);
                    _MouseTrackSpeed = value;
                }
                catch (Exception err)
                {
                    MessageBox.Show("Apply Mouse Tracking Speed False!" + Environment.NewLine + "Error:" + err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private int _WheelScrollLines = 1;
        /// <summary>
        /// 行滚动速度
        /// 默认为注册表的值
        /// 无法读取注册表时为1
        /// </summary>
        public int WheelScrollLines
        {
            get { return _WheelScrollLines; }
            set
            {
                try
                {
                    UpdateSysParamInfo(0x0069, value, 0, false);
                    _WheelScrollLines = value;
                }
                catch (Exception err)
                {
                    MessageBox.Show("Apply Wheel Scroll Lines False!" + Environment.NewLine + "Error:" + err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private int _WheelScrollChars = 1;
        /// <summary>
        /// 列滚动速度
        /// 默认为注册表的值
        /// 无法读取注册表时为1
        /// </summary>
        public int WheelScrollChars
        {
            get { return _WheelScrollChars; }
            set
            {
                try
                {
                    UpdateSysParamInfo(0x006d, value, 0, false);
                    _WheelScrollChars = value;
                }
                catch (Exception err)
                {
                    MessageBox.Show("Apply Wheel Scroll Chars False!" + Environment.NewLine + "Error:" + err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        /// <summary>
        /// 触摸板当前标志位值
        /// </summary>
        private int _Opt = 105;
        /// <summary>
        /// 触摸板当前标志位
        /// </summary>
        public Child Options
        {
            get { return (Child)_Opt; }
            set
            {
                if (!TrackpadOptionsRW(AMTPDeviceURL, ref _Opt, true))
                {
                    throw new Exception("Apply Device Config False");
                }
                _Opt = (int)value;
            }
        }

        public MyFunction() { }
        public MyFunction(bool ReadPadConfig)
        {
            try
            {
                _MouseTrackSpeed = ReadRegistryValue(Registry.CurrentUser, MouseRegSubKey, MouseTrackSpeedRegName);
                _WheelScrollLines = ReadRegistryValue(Registry.CurrentUser, WheelScrollRegSubKey, WheelScrollLineRegName);
                _WheelScrollChars = ReadRegistryValue(Registry.CurrentUser, WheelScrollRegSubKey, WheelScrollCharRegName);

                if (ReadPadConfig)
                {
                    bool status = TrackpadOptionsRW(AMTPDeviceURL, ref _Opt, false);
                    if (!status)
                        throw new Exception("Load Confign From Device False");
                }
                else
                {
                    _Opt = ReadRegistryValue(Registry.CurrentUser, AMTPDeviceRegSubKey, AMTPDeviceRegName);
                }
            }
            catch (Exception err)
            {
                throw new Exception("Load Config Error", err);
            }
        }

        #region 结构体和api
        /// <summary>
        /// 触摸板状态标志位
        /// </summary>
        [Flags]
        public enum Child
        {
            /// <summary>
            /// 允许触摸单击
            /// 单指左键单击
            /// </summary>
            TapToClick = 0x01,
            /// <summary>
            /// 单指双击拖拽
            /// </summary>
            Dragging = 0x02,
            /// <summary>
            /// 锁定拖拽,单击后释放
            /// Dragging无效时该项无效
            /// </summary>
            DragLock = 0x04,
            /// <summary>
            /// 双指滚动
            /// </summary>
            Scroll = 0x08,
            Unknow = 0x10,
            /// <summary>
            /// 双指触摸右键单击
            /// TapToClick无效时该项无效
            /// </summary>
            RightClick = 0x20,
            /// <summary>
            /// 启用实体按键右键单击
            /// 默认实体双键均为左键
            /// </summary>
            RightButton = 0x40,
            /// <summary>
            /// 交换左右实体按键
            /// </summary>
            SwapButton = 0x80
        }

        /// <summary>
        /// 触摸板属性结构体
        /// </summary>
        [StructLayout(LayoutKind.Explicit, Size = 4)]
        private struct DataStruct
        {
            [FieldOffset(0)]
            public Child child;
            [FieldOffset(0)]
            public UInt32 mode;
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr CreateFile(
            string lpFileName,
            uint dwDesiredAccess,
            uint dwShareMode,
            IntPtr SecurityAttributes,
            uint dwCreationDisposition,
            uint dwFlagsAndAttributes,
            IntPtr hTemplateFile
        );

        [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", CharSet = CharSet.Ansi, SetLastError = true)]
        private static extern Boolean DeviceIoControl(
            IntPtr hDevice,
            int dwIoControlCode,
           byte[] lpInBuffer,
            int nInBufferSize,
           out DataStruct lpOutBuffer,
            int nOutBufferSize,
            out  uint lpBytesReturned,
            IntPtr lpOverlapped
            );

        [DllImport("kernel32.dll", EntryPoint = "CloseHandle", CharSet = CharSet.Ansi)]
        private static extern bool CloseHandle(IntPtr hDevice);

        [DllImport("user32.dll", EntryPoint = "SystemParametersInfoA", CharSet = CharSet.Ansi, ExactSpelling = true)]
        private static extern bool SystemParametersInfo(int uAction, int uParam, int lpvParam, int fuWinIni);

        #endregion

        public bool UpdateSysParamInfo(int uAction, int uParam, int lpvParam, bool save = false)
        {
            if (save)
                return SystemParametersInfo(uAction, uParam, lpvParam, 1);
            else
                return SystemParametersInfo(uAction, uParam, lpvParam, 0);
        }

        /// <summary>
        /// 读取注册表中存储的参数
        /// </summary>
        /// <param name="RootKey">主键</param>
        /// <param name="SubKey">子健</param>
        /// <param name="Name">名称</param>
        /// <returns>操作状态</returns>
        public int ReadRegistryValue(RegistryKey RootKey, string SubKey, string Name)
        {
            using (RegistryKey key = RootKey.OpenSubKey(SubKey))
            {
                if (key == null)
                {
                    throw new ArgumentNullException("The Registry SubKey Not Exist!");
                }
                object ItemValue = key.GetValue(Name);
                key.Close();
                return Convert.ToInt32(ItemValue);
            }
        }

        /// <summary>
        /// 参数写进注册表
        /// </summary>
        /// <param name="RootKey">主键</param>
        /// <param name="SubKey">子健</param>
        /// <param name="Name">名称</param>
        /// <param name="Value">值</param>
        /// <param name="ValueType">值类型</param>
        /// <returns>操作状态</returns>
        public bool WriteRegistryValue(RegistryKey RootKey, string SubKey, string Name, int Value, RegistryValueKind ValueType = RegistryValueKind.DWord)
        {
            using (RegistryKey key = RootKey.OpenSubKey(SubKey, true))
            {
                if (key == null)
                {
                    key.CreateSubKey(SubKey);
                }
                key.SetValue(Name, Value, ValueType);
                key.Close();
                return true;
            }
        }

        /// <summary>
        /// 触控板参数读写
        /// </summary>
        /// <param name="DeviceUrl">触控板硬件路径</param>
        /// <param name="DeviceOpt">触控板参数</param>
        /// <param name="Write">写入还是读取</param>
        /// <returns>操作状态</returns>
        public bool TrackpadOptionsRW(string DeviceUrl, ref int OptFlags, bool Write = false)
        {
            uint br = 0;//When Read DeviceOpt this = Size of DeviceOpt, When Write this is Unuseful.
            IntPtr tp = CreateFile(AMTPDeviceURL, 0x80000000 | 0x40000000/*GENERIC_READ | GENERIC_WRITE*/, 0x00000001 | 0x00000002/* FILE_SHARE_READ |FILE_SHARE_WRITE*/, IntPtr.Zero, 3/*OPEN_EXISTING*/, 0, IntPtr.Zero);
            if (new IntPtr(-1) == tp)
            {
                //MessageBox.Show("Not Find Device!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
                throw new Exception("Not Find Device!");
            }
            bool sta = false;
            if (!Write)
            {
                DataStruct data = new DataStruct();
                byte[] junk = new byte[0];
                //IOConsoleCode
                //#define CTL_CODE( DeviceType, Function, Method, Access ) (\ ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \)
                //CTL_CODE(FILE_DEVICE_MOUSE, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)=0xf2000
                sta = DeviceIoControl(tp, 0xf2000, junk, 0, out data, Marshal.SizeOf(data), out br, IntPtr.Zero);
                //int err = Marshal.GetLastWin32Error();
                if (br != Marshal.SizeOf(data))
                {
                    throw new OverflowException("Size of Device Options is Not Equal Returned Bytes!");
                    //MessageBox.Show("Size of Device Options is Not Equal Returned Bytes! Data Maybe Error!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
                OptFlags = (int)data.mode;
            }
            else
            {
                byte[] buffer = BitConverter.GetBytes(OptFlags);
                DataStruct junk = new DataStruct();
                //IOConsoleCode
                //CTL_CODE(FILE_DEVICE_MOUSE, 0x0801, METHOD_BUFFERED, FILE_ANY_ACCESS)= 0xf2004
                sta = DeviceIoControl(tp, 0xf2004, buffer, buffer.Length, out junk, Marshal.SizeOf(junk), out br, IntPtr.Zero);
                //int err = Marshal.GetLastWin32Error();
            }
            if (!CloseHandle(tp))
            {
                throw new Exception("Close Device Handle False!");
                //MessageBox.Show("Close Device Handle False!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            return sta;
        }

        public bool SaveAllConfig()
        {
            if (!UpdateSysParamInfo(0x0071, 0, _MouseTrackSpeed, true))
                return false;
            if (!UpdateSysParamInfo(0x0069, _WheelScrollChars, 0, true))
                return false;
            if (!UpdateSysParamInfo(0x006d, _WheelScrollChars, 0, true))
                return false;
            if (!WriteRegistryValue(Registry.CurrentUser, AMTPDeviceRegSubKey, AMTPDeviceRegName, _Opt))
                return false;
            return true;
        }
    }
}
